local maxDist = 85
local holdDist = 60
local stability = 100

local function drop(ply)

	local wep = ply:GetActiveWeapon()
	
	if (ValidEntity(ply.holdEnt) && ValidEntity(wep)) then
	
		wep:SetNextPrimaryFire(CurTime())
		wep:SetNextSecondaryFire(CurTime())
	end

	ply.holdEnt = NULL
	
	if (!ply.visibleGun) then
	
		ply:DrawViewModel(true)
		ply:DrawWorldModel(true)
	end
	
	ply:SendLua( "gui.EnableScreenClicker(false)" )
	ply.hasDropped = true
end

local function drag(ply, key)

	if (key == IN_USE) then
	
		ply.hasDropped = false;
		
		if (ValidEntity(ply.holdEnt)) then
			
			drop(ply)
			return
		else
		
			local tr = {}
			tr.start = ply:GetShootPos()
			tr.endpos = tr.start + ply:GetCursorAimVector() * maxDist
			tr.filter = ply
			
			tr = util.TraceLine(tr)
			
			if (!ValidEntity(ply:GetVehicle()) && ValidEntity(tr.Entity) && (tr.Entity:GetMoveType() == MOVETYPE_VPHYSICS || tr.Entity:IsPlayer())) then
			
				ply.holdEnt = tr.Entity
				ply.holdPhys = tr.PhysicsBone
				ply.holdOffset = tr.Entity:WorldToLocal(tr.HitPos)
								
				if (ValidEntity(ply:GetActiveWeapon())) then
				
					ply.invisibleGun = !ply:GetActiveWeapon():IsWeaponVisible()
					ply.weapon = ply:GetActiveWeapon():GetClass()
				end
																
				local phys = tr.Entity:GetPhysicsObjectNum(tr.PhysicsBone)
			end
		end
		
	elseif (SinglePlayer() && key == IN_RELOAD && ValidEntity(ply.holdEnt)) then
	
		ply.mouseMovement = ply.mouseMovement || false
		
		ply:SendLua( "gui.EnableScreenClicker("..tostring(!ply.mouseMovement)..")" )
		ply.mouseMovement = !ply.mouseMovement
	end
end

hook.Add("KeyPress","drag",drag)

local function dragThink()

	for _, v in pairs(player.GetAll()) do
	
		if (ValidEntity(v.holdEnt)) then
					
			local wep = v:GetActiveWeapon()
			
			if (ValidEntity(wep)) then
			
				wep:SetNextPrimaryFire(CurTime()+10)
				wep:SetNextSecondaryFire(CurTime()+10)
				v:DrawViewModel(false)
				v:DrawWorldModel(false)
				
				if (wep:GetClass() != v.weapon) then
				
					v:SelectWeapon(v.weapon)
				end
			end
			
			if (v:GetGroundEntity() == v.holdEnt || ValidEntity(v:GetVehicle()) || (ValidEntity(v:GetActiveWeapon()) && (v:GetActiveWeapon():GetClass() == "weapon_physgun" || v:GetActiveWeapon():GetClass() == "weapon_toolgun" || v:GetActiveWeapon():GetClass() == "gmod_camera"))) then
			
				drop(v)
				return
			end
			
			local phys1
			
			if (v.holdEnt:IsPlayer()) then
			
				phys1 = v.holdEnt:GetPhysicsObject()
			else
		
				phys1 = v.holdEnt:GetPhysicsObjectNum(v.holdPhys)
			end
			
			local phys2 = v:GetPhysicsObject()
			
			local pos1 = v:GetShootPos() + v:GetCursorAimVector() * holdDist
			local pos2
			
			if (v.holdEnt:GetClass() == "prop_ragdoll") then
			
				pos2 = phys1:GetPos()
			else
			
				pos2 = v.holdEnt:LocalToWorld(v.holdOffset)
			end
												
			if (pos1:Distance(pos2) > maxDist) then
			
				drop(v)
				return
			else
			
				local mass1 = v.holdEnt:GetPhysicsObject():GetMass()
				local mass2 = phys2:GetMass()
				
				local norm = pos2 - pos1
				
				local vel1 = norm * mass1/(mass1+mass2)
				local vel2 = norm * -mass2/(mass1+mass2)
				
				local damp1 = phys2:GetVelocity():GetNormalized() * math.sqrt(2*phys2:GetVelocity():Length())
				local damp2 = phys1:GetVelocity():GetNormalized() * math.sqrt(2*phys1:GetVelocity():Length())
				
				vel1 = vel1 - damp1
				vel2 = vel2 - damp2
												
				v:SetLocalVelocity(vel1*(v.strengthLevel && v.strengthLevel/100 || 1)+v:GetVelocity())
				
				if (v.holdEnt:IsPlayer()) then
				
					v.holdEnt:SetLocalVelocity(vel2*(v.strengthLevel && v.strengthLevel/100 || 1)+v.holdEnt:GetVelocity())
				else
													
					phys1:ApplyForceOffset(vel2*mass1*(v.strengthLevel && v.strengthLevel/100 || 1)/2,pos2+phys1:GetAngle():Right()*stability)
					phys1:ApplyForceOffset(vel2*mass1*(v.strengthLevel && v.strengthLevel/100 || 1)/2,pos2-phys1:GetAngle():Right()*stability)
				end
			end
			
		elseif (!v.hasDropped) then
		
			drop(v)
		end
	end
end

hook.Add("Think","drag",dragThink)